/*	Copyright 2007 - Xavier Baro (xbaro@cvc.uab.cat)

	This file is part of eapmlib.

    Eapmlib is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 3 of the License, or any 
	later version.

    Eapmlib is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/
#include "SamplesSet.h"
#include "ImageData.h"
#include <fstream>
#include <iostream>
#include <algorithm>
#include <functional>

Evolutive::CSamplesSet::CSamplesSet(void) : m_SamplesSet(NULL),m_NumChannels(0),m_ImgDepth(0),m_NumImages(0),m_DataLen(0),m_ExpectedValue(NULL),m_Detector(NULL),m_IdxBGFile(-1),m_AvailableBGRegions(0)
{
#ifndef USE_OPENCV
	OPENCV_ERROR_CLASS("CSamplesSet");
#else
	//! By default select unsigned byte
	m_ImgDepth=IPL_DEPTH_8U;

	//! By default don't use the color information
	m_UseColor=false;		

	//! Set a default size
	m_ImgSize=cvSize(32,32);
#endif
}

Evolutive::CSamplesSet::~CSamplesSet(void)
{
	//! Remove allocated memory
	if(m_ExpectedValue)
		delete[] m_ExpectedValue;	
	if(m_SamplesSet)
		delete [] m_SamplesSet;
}

std::ostream& operator<<(std::ostream &stream, Evolutive::CSamplesSet C)
{
	//! Save the data
	//C.Serialize((iostream*)&stream,true);

	return stream;
}

std::istream& operator>>(std::istream &stream, Evolutive::CSamplesSet C)
{
  	//! Load the data
	//C.Serialize((iostream*)&stream,false);

	return stream;
}

#ifdef USE_OPENCV
void Evolutive::CSamplesSet::Serialize(std::fstream &fs,bool Saving)
{
	if(Saving)
	{
		//! Save the data to a file
		//! Save the class attributes
		fs.write((char*)&m_UseColor,1);
		fs.write((char*)&m_NumChannels,sizeof(int));
		fs.write((char*)&m_NumImages,sizeof(int));		
		fs.write((char*)&m_ImgDepth,sizeof(int));
		fs.write((char*)&m_ImgSize,sizeof(m_ImgSize));
		fs.write((char*)&m_DataLen,sizeof(int));	
				
		if(m_NumImages>0)
		{
			//! Save the images	
			fs.write((char*)m_SamplesSet,m_NumImages*m_DataLen);

			//! Save the expected values	
			fs.write((char*)m_ExpectedValue,m_NumImages*sizeof(double));
		}
	}
	else
	{
		//! Load the data from a file
		//! Load the class attributes
		fs.read((char*)&m_UseColor,1);	
		fs.read((char*)&m_NumChannels,sizeof(int));
		fs.read((char*)&m_NumImages,sizeof(int));
		fs.read((char*)&m_ImgDepth,sizeof(int));
		fs.read((char*)&m_ImgSize,sizeof(m_ImgSize));
		fs.read((char*)&m_DataLen,sizeof(int));
		
		//! Create the data storage for these images
		if(m_SamplesSet)
			delete[] m_SamplesSet;		
		m_SamplesSet=NULL;
		if(m_ExpectedValue)
			delete[] m_ExpectedValue;
		m_ExpectedValue=NULL;

		//! If there are images, load them and their expected values
		if(m_NumImages>0)
		{
			//! Load the images
			m_SamplesSet=new unsigned char[m_DataLen*m_NumImages];
			fs.read((char*)m_SamplesSet,m_DataLen*m_NumImages);

			//! Load the expected values
			m_ExpectedValue=new double[m_NumImages];
			fs.read((char*)m_ExpectedValue,m_NumImages*sizeof(double));
		}
	}	
}

void Evolutive::CSamplesSet::Load(std::string FileName)
{
	fstream ifs;
		
	//! Open the input file
	ifs.open(FileName.data(),ios_base::in | ios_base::binary);
	if(ifs.fail())
		throw CEvolutiveLibException("Cannot open input file",__FILE__,__LINE__,"Load");

	//! Recover the data
	Serialize(ifs,false);

	//! Close the file
	ifs.close();
}

void Evolutive::CSamplesSet::Save(std::string FileName)
{
	fstream ofs;
	
	//! Create the output file
	ofs.open(FileName.data(),ios_base::out | ios_base::trunc | ios_base::binary);
	if(ofs.fail())
		throw CEvolutiveLibException("Cannot open output file",__FILE__,__LINE__,"Save");

	//! Save the data
	Serialize(ofs,true);

	//! Close the file
	ofs.flush();
	ofs.close();
}

void Evolutive::CSamplesSet::Show(int WaitTime,int WndType)
{
	IplImage *Img=NULL;
	register int i;
	bool Show;
	int KeyVal;

	//! If there are no images return
	if(m_NumImages==0)
		return;

	//! Create a header
	Img=cvCreateImageHeader(m_ImgSize,m_ImgDepth,m_NumChannels);

	//! Create the output window
	cvNamedWindow("Samples Set",WndType);

	//! Show all the images from the set
	Show=true;
	for(i=0;i<m_NumImages;i++)
	{
		//! If the show flag is disabled exit
		if(!Show)
			break;

		//! Point the header to the data
		Img->imageData=(char*)&(m_SamplesSet[m_DataLen*i]);

		//! Show the image		
		cvShowImage("Samples Set",Img);

		//! Wait
		KeyVal=cvWaitKey(WaitTime);

		//! If the user press ESC key exit
		if(KeyVal==27)
			Show=false;
	}

	//! Remove the output window
	cvDestroyWindow("Samples Set");
}

int Evolutive::CSamplesSet::GetNumSamples(void)
{
	return m_NumImages;
}

bool Evolutive::CSamplesSet::GetColorInfo(void)
{
	return m_UseColor;
}

CvSize Evolutive::CSamplesSet::GetSamplesSize(void)
{
	return m_ImgSize;
}

Evolutive::CData* Evolutive::CSamplesSet::operator[] (unsigned int Idx)
{
	return GetData(Idx);
}

Evolutive::CData* Evolutive::CSamplesSet::GetData(unsigned int Idx)
{
	CImageData *Data=new CImageData(GetImage(Idx));	
	
	return (CData*)Data;
}

IplImage* Evolutive::CSamplesSet::GetImage(unsigned int Idx)
{
	IplImage *Img=NULL;
	
	//! Verify the data
	if(!m_SamplesSet)
		return NULL;

	//! Create a header
	Img=cvCreateImageHeader(m_ImgSize,m_ImgDepth,m_NumChannels);

	//! Point the header to the data
	Img->imageData=(char*)&(m_SamplesSet[m_DataLen*Idx]);
	return Img;
}


int Evolutive::CSamplesSet::GetNumImagesFromFile(std::string FileName)
{
	int NumImages;
	char Buffer[1024];

	//! Open the file
	ifstream ifs(FileName.data());
	if(ifs.fail())
		throw CEvolutiveLibException("Cannot open input file",__FILE__,__LINE__,"GetNumImagesFromFile");

	//! Read the number of images in the file	
	NumImages=0;	
	while(!ifs.eof())
	{		
		//! Read the line
		ifs.getline(Buffer,1024);		

		//! If we have an empty line continue
		if(!ifs.gcount())
			continue;		
		
		//! Increment the counter
		NumImages++;
	}

    //! Close the file
    ifs.close();

	return NumImages;
}

void Evolutive::CSamplesSet::LoadImages(int NumClasses,std::string FileName[],double ClassValue[],int Width,int Height)
{
	int TotalImages;
	register int i;
	int Index;

	//! Initialize the image counter
	TotalImages=0;
	for(i=0;i<NumClasses;i++)
	{
		TotalImages+=GetNumImagesFromFile(FileName[i]);
	}

	//! Store the given size
	m_ImgSize.width=Width;
	m_ImgSize.height=Height;

	//! Prepare class storage
	AllocateImagesMemory(TotalImages);

	//! Read all the images and add them to the class
	Index=0;
	for(i=0;i<NumClasses;i++)
	{
		//! Append the images from file
		Index=AppendImagesFromFile(FileName[i],ClassValue[i],Index);
	}
}

void Evolutive::CSamplesSet::LoadImages(std::string ObjectFile,std::string BGFile,int NumNeg,int Width,int Height)
{
	int TotalImages;
	int Index;

	//! Obtain the number of positive samples
	TotalImages=GetNumImagesFromFile(ObjectFile);
	
	//! Adds the negative samples
	TotalImages+=NumNeg;

	//! Store the given size
	m_ImgSize.width=Width;
	m_ImgSize.height=Height;

	//! Prepare class storage
	AllocateImagesMemory(TotalImages);

	//! Append the positive samples from file
	Index=AppendImagesFromFile(ObjectFile,1.0,0);
	
	//! Append the negative Samples
	AppendPatches(BGFile,NumNeg,Index);
}

void Evolutive::CSamplesSet::LoadImages(std::string ImgFileName,std::string LabelFileName,int Width,int Height)
{
	char Buffer[1024];
	int TotalImages;
	int Index;
	ifstream ifs;

	//! Obtain the number of positive samples
	TotalImages=GetNumImagesFromFile(ImgFileName);
	
	//! Verify the number of labels
	if(GetNumImagesFromFile(LabelFileName)!=TotalImages)
		throw CEvolutiveLibException("Images file and labels file has different number of rows",__FILE__,__LINE__,"LoadImages");

	//! Store the given size
	m_ImgSize.width=Width;
	m_ImgSize.height=Height;
	
	//! Prepare class storage
	AllocateImagesMemory(TotalImages);

	//! Append the positive samples from file
	AppendImagesFromFile(ImgFileName,1.0,0);
	
	//! Open the labels file
	ifs.open(LabelFileName.data());
	if(ifs.fail())
		throw CEvolutiveLibException("Cannot open input file",__FILE__,__LINE__,"LoadImages");

	Index=0;
	while(!ifs.eof())
	{
		//! Read the line
		ifs.getline(Buffer,1024);

		//! If we have an empty line continue
		if(!ifs.gcount())
			continue;	

		//! Sets the class		
		m_ExpectedValue[Index]=atof(Buffer);

		//! Increment the index
		Index++;
	}	

	//! Close the labels file
	ifs.close();
}

int Evolutive::CSamplesSet::AppendImagesFromFile(std::string FileName,double ExpectedVal,int FirstIndex)
{
	char Buffer[1024];
	IplImage *pImg=NULL,*pSmallImg=NULL;
	ifstream ifs;
	
	//! Open the file
	ifs.open(FileName.data());
	if(ifs.fail())
		throw CEvolutiveLibException("Cannot open input file",__FILE__,__LINE__,"AppendImagesFromFile");

	//! Create a destination image to resize the input images
	pSmallImg=cvCreateImage(m_ImgSize,m_ImgDepth,m_NumChannels);
	if(!pSmallImg)
			throw CEvolutiveLibException("Cannot create the image",__FILE__,__LINE__,"AppendImagesFromFile");		

	//! Load all the images and store them to the class storage	
	while(!ifs.eof())
	{
		//! Read the line
		ifs.getline(Buffer,1024);

		//! If we have an empty line continue
		if(!ifs.gcount())
			continue;	

		//! Load the image
		pImg=cvLoadImage(Buffer,m_UseColor);
		if(!pImg)
			throw CEvolutiveLibException("Cannot load the image",__FILE__,__LINE__,"AppendImagesFromFile");		

		//! Resize the image to the output size, without deformation
		ChangeImageSize(pImg,pSmallImg);

		//! Copy the image data to the corresponding column
		memcpy(&(m_SamplesSet[m_DataLen*FirstIndex]),pSmallImg->imageData,m_DataLen);

		//! Remove the image
		cvReleaseImage(&pImg);

		//! Increment the counter
		FirstIndex++;
	}

    //! Close the file
    ifs.close();

	//! Remove the image
	cvReleaseImage(&pSmallImg);


	return FirstIndex;
}

void Evolutive::CSamplesSet::AllocateImagesMemory(int NumImages)
{
	IplImage *pRefImgHeader=NULL;

	//! If the color flag is enables set the number of channels to 3
	if(m_UseColor)
		m_NumChannels=3;
	else
		m_NumChannels=1;	

	//! Create an image header to obtain the images size		
	pRefImgHeader=cvCreateImageHeader(m_ImgSize,m_ImgDepth,m_NumChannels);
	m_DataLen=pRefImgHeader->imageSize;
	cvReleaseImageHeader(&pRefImgHeader);
	
	//! Remove old allocated memory
	if(m_SamplesSet)
		delete[] m_SamplesSet;	
	if(m_ExpectedValue)
		delete[] m_ExpectedValue;

	//! Allocate new memory
	m_SamplesSet=new unsigned char[NumImages*m_DataLen];
	m_ExpectedValue=new double[NumImages];

	//! Store the number of images
	m_NumImages=NumImages;
}

double Evolutive::CSamplesSet::ExpectedVal(unsigned int Idx)
{
	return m_ExpectedValue[Idx];
}

void Evolutive::CSamplesSet::SetExpectedValue(unsigned int Idx,double Value)
{
	m_ExpectedValue[Idx]=Value;
}

void Evolutive::CSamplesSet::SetExpectedValue(unsigned int IdxFirst,unsigned int IdxLast,double Value)
{
	register unsigned int i;

	for(i=IdxFirst;i<=IdxLast;i++)
	{
		m_ExpectedValue[i]=Value;
	}
}

int Evolutive::CSamplesSet::GetNumDiffValues(int **pNumSamples,double **pValues)
{
	register int i;
	int Count;
	double *ValuesCopy=NULL,*pVal=NULL;
	double LastVal;
	int *pCount=NULL;

	//! Check if we have some image
	if(!m_NumImages)
	{
		if(pNumSamples)
		{
			if(pNumSamples[0])
				delete[] pNumSamples[0];
			pNumSamples[0]=NULL;
		}
		if(pValues)
		{
			if(pValues[0])
				delete[] pValues[0];
			pValues[0]=NULL;
		}
		return 0;
	}

	//! Create a copy of the expected values
	ValuesCopy=new double[m_NumImages];
	memcpy(ValuesCopy,m_ExpectedValue,m_NumImages*sizeof(double));

	//! Sort the values
	qsort(ValuesCopy,m_NumImages,sizeof(double),DblCompareAsc);

	//! Find the number of different values
	LastVal=ValuesCopy[0];
	Count=1;
	for(i=0;i<m_NumImages;i++)
	{
		if(LastVal!=ValuesCopy[i])
		{
			Count++;
			LastVal=ValuesCopy[i];
		}
	}
	
	//! If the pointer is set, allocate new memory
	if(pNumSamples || pValues)
	{
		//! Allocate the output data
		if(pNumSamples)
		{
			if(pNumSamples[0])
				delete[] pNumSamples[0];
			pNumSamples[0]=new int[Count];
			pCount=pNumSamples[0];
		}
		if(pValues)
		{
			if(pValues[0])
				delete[] pValues[0];
			pValues[0]=new double[Count];
			pVal=pValues[0];			
		}			

		//! Copy the values
		LastVal=ValuesCopy[0];
		Count=1;		
		if(pVal)
			pVal[0]=LastVal;		
		if(pCount)
			pCount[0]=0;
		for(i=0;i<m_NumImages;i++)
		{
			if(LastVal!=ValuesCopy[i])
			{				
				Count++;
				LastVal=ValuesCopy[i];
				if(pVal)
				{
					pVal++;
					pVal[0]=LastVal;
				}
				if(pCount)
				{
					pCount++;
					pCount[0]=1;
				}
			}
			else
			{
				if(pCount)
					*pCount=(*pCount)+1;
			}
		}
	}

	//! Remove the allocated memory
	delete[] ValuesCopy;

	return Count;
}

void Evolutive::CSamplesSet::SplitData(CSamplesSet *Train,CSamplesSet *Test,double TestPer)
{
	register int i,c;	
	int NumClasses;
	double *Vals=NULL;
	int *Counts=NULL;
	int *TestNumSamples=NULL;
	int TotalTest;
	int ValIdx;
	int TestAdd,TrainAdd;
	IplImage *pSource=NULL,*pDest=NULL;
	bool *Visited=NULL;
	int *IdxVec=NULL,*IdxVecTest=NULL,*IdxVecTrain=NULL;
	int RndStep,Pos;
	
    // Verify the input parameters
	if(!Train || !Test)
		throw CEvolutiveLibException("NULL output samples set pointers",__FILE__,__LINE__,"SplitData");

	// Count the number of different class labels
	NumClasses=GetNumDiffValues(&Counts,&Vals);

	//! Create a vector with the test samples for each class
	TestNumSamples=new int[NumClasses];

	//! Calculate the final distribution
	TotalTest=0;
	for(i=0;i<NumClasses;i++)
	{
		TestNumSamples[i]=cvRound(TestPer*Counts[i]);
		TotalTest+=TestNumSamples[i];
	}

	// Create a vector of used indexes
	Visited=new bool[m_NumImages];
	memset(Visited,0,sizeof(bool)*m_NumImages);

	// Create the indexes vectors
	IdxVec=new int[m_NumImages];
	IdxVecTest=new int[TotalTest];
	IdxVecTrain=new int[m_NumImages-TotalTest];	

	// Fill a vector with all the indexes, with random order
	Pos=0;
	for(i=0;i<m_NumImages;i++)
	{
		// Generate a random step
		RndStep=floor((double)rand()/RAND_MAX*(double)m_NumImages);

		// Move the position using the step
		Pos+=RndStep;

		// If the final position exceeds the maximum, count from the begining
		if(Pos>=m_NumImages)
			Pos-=m_NumImages;

		// Find the first next position that is not visited
		while(Visited[Pos])
		{
			Pos++;
			if(Pos>=m_NumImages)
				Pos=0;
		}

		// Assign this index as the new position
		Visited[Pos]=true;
		IdxVec[i]=Pos;
	}

	// Create the list of indexes for training and test.
	TestAdd=0;
	TrainAdd=0;
	for(i=0;i<m_NumImages;i++)
	{
		// Get the index
		Pos=IdxVec[i];

		// Find the index for this class value
		ValIdx=-1;
		for(c=0;c<NumClasses;c++)
		{
			if(m_ExpectedValue[Pos]==Vals[c])
			{
				ValIdx=c;
				break;
			}
		}

		// Check the index value
		if(ValIdx<0)
			throw CEvolutiveLibException("Expected value not found",__FILE__,__LINE__,"SplitData");

		// If the test set is complete for this value add it to the train set
		if(TestNumSamples[ValIdx]>0)
		{
			// Add to the test set			
			IdxVecTest[TestAdd]=Pos;
			TestNumSamples[ValIdx]--;
			TestAdd++;			
		}
		else
		{
			// Add to the train set
			IdxVecTrain[TrainAdd]=Pos;
			TrainAdd++;
		}
	}

	// Copy the output parameters and allocate the destination data
	CopyParameters(Train,m_NumImages-TotalTest);
	CopyParameters(Test,TotalTest);
	
	// Copy the test data
	for(i=0;i<TotalTest;i++)
	{
		//! Point the source data		
		pSource=GetImage(IdxVecTest[i]);

		//! Point to the destination data				
		pDest=Test->GetImage(i);

		//! Copy the data
		cvCopy(pSource,pDest);		

		//! Copy the expected value
		Test->m_ExpectedValue[i]=m_ExpectedValue[IdxVecTest[i]];
	}

	// Copy the train data
	for(i=0;i<m_NumImages-TotalTest;i++)
	{
		//! Point the source data		
		pSource=GetImage(IdxVecTrain[i]);

		//! Point to the destination data				
		pDest=Train->GetImage(i);

		//! Copy the data
		cvCopy(pSource,pDest);

		//! Copy the expected value
		Train->m_ExpectedValue[i]=m_ExpectedValue[IdxVecTrain[i]];
	}

	// Clear the temporal data	
	delete[] Vals;
	delete[] Counts;
	delete[] TestNumSamples;
	delete[] Visited;
	delete[] IdxVec;
	delete[] IdxVecTest;
	delete[] IdxVecTrain;
	cvReleaseImageHeader(&pDest);
	cvReleaseImageHeader(&pSource);
}


void Evolutive::CSamplesSet::CopyParameters(CSamplesSet *Object,int NumSamples)
{
	//! Copy the value of fixed parameters
	Object->m_UseColor=m_UseColor;
	Object->m_ImgDepth=m_ImgDepth;
	Object->m_ImgSize=m_ImgSize;

	//! Allocate the destination data
	Object->AllocateImagesMemory(NumSamples);
}

bool Evolutive::CSamplesSet::ChangeImageSize(IplImage *Source,IplImage *Destination)
{
	double ARIn,AROut;
	CvRect ROI;
	int CutRegion;

	// Calculate the aspect ratio for the input and output images
	ARIn=((double)Source->width)/((double)Source->height);
	AROut=((double)Destination->width)/((double)Destination->height);

	// If the images have the same aspect ratio only a resize operation is needed.
	if(fabs(ARIn-AROut)<EPS)
	{
		// Resize the image
		cvResize(Source,Destination,CV_INTER_CUBIC);
		return true;
	}

	// Set the initial ROI
	ROI.x=0;
	ROI.y=0;
	ROI.width=Source->width;
	ROI.height=Source->height;

	// Find the side to be cropped
	if(ARIn<AROut)
	{
		// Reduce the height to increase the AR
		if(ARIn>1)
			ROI.height=cvRound(((double)Source->width)/ARIn);
		else
			ROI.height=cvRound(((double)Source->width)*ARIn);

		// Calculate the region to cut
		CutRegion=cvRound(Source->height-ROI.height);

		// Displace the image
		ROI.y=ceil((double)CutRegion/2.0);
	}
	else
	{
		// Reduce the width to reduce the AR
		if(ARIn>1)
			ROI.width=cvRound(((double)Source->height)/ARIn);		
		else
			ROI.width=cvRound(((double)Source->height)*ARIn);

		// Calculate the region to cut
		CutRegion=cvRound(Source->width-ROI.width);

		// Displace the image
		ROI.x=ceil((double)CutRegion/2.0);
	}

	// Select the region on the source image
	cvSetImageROI(Source,ROI);

	// Resize the image
	cvResize(Source,Destination,CV_INTER_CUBIC);

	// Remove the region on the source image
	cvResetImageROI(Source);
		
	// If the output size is bigger than the input size return false.
	if(Destination->width>Source->width || Destination->height>Source->height)
		return false;
	else
		return true;
}

bool Evolutive::CSamplesSet::GetBinaryDist(int &NumPos,int &NumNeg)
{
	double *Labels=NULL;
	int *NumSamples=NULL;
	int NumClass;
	bool RetVal;
	
	// Obtain the number of samples and the number of samples in each class
	NumClass=GetNumDiffValues(&NumSamples,&Labels);

	// Initialize the output values
	NumPos=0;
	NumNeg=0;

	// Verify if the samples set is binary
	if(NumClass==2)
	{
		RetVal=true;
		if(Labels[0]>0)
		{
			NumPos=NumSamples[0];
			NumNeg=NumSamples[1];
		}
		else
		{
			NumPos=NumSamples[1];
			NumNeg=NumSamples[0];
		}
	}
	else
	{
		RetVal=false;
	}

	// Release the used memory
	delete[] NumSamples;
	delete[] Labels;

	return RetVal;
}

void Evolutive::CSamplesSet::AppendPatches(std::string FileName,int NumPatches,int FirstIndex)
{
	//TODO: Create samples by using patches
	throw CEvolutiveLibException("TODO: Undefined function",__FILE__,__LINE__,"AppendPatches");
}

void Evolutive::CSamplesSet::MergeData(CSamplesSet *Samples1,CSamplesSet *Samples2)
{
	CvSize Size1,Size2;
	int NumSamples;
	
	// Verify if both samples set have the same image sizes
	Size1=Samples1->GetSamplesSize();
	Size2=Samples2->GetSamplesSize();
	if(Size1.width!=Size2.width || Size1.height!=Size2.height)
		CEvolutiveLibException("Both Sample Sets must have identical image sizes",__FILE__,__LINE__,"MergeData");

	// Verify the images type and channels
	if(Samples1->m_UseColor!=Samples2->m_UseColor || Samples1->m_ImgDepth!=Samples2->m_ImgDepth)
		CEvolutiveLibException("Both Sample Sets must have identical image types",__FILE__,__LINE__,"MergeData");

	// Obtain the number of images
	NumSamples=Samples1->GetNumSamples()+Samples2->GetNumSamples();

	// Copy the samples parameters
	m_UseColor=Samples1->m_UseColor;
	m_ImgDepth=Samples1->m_ImgDepth;
	m_ImgSize=Samples1->m_ImgSize;

	// Allocate the destination data
	AllocateImagesMemory(NumSamples);

	// Copy the expected values
	memcpy(m_ExpectedValue,Samples1->m_ExpectedValue,sizeof(double)*Samples1->GetNumSamples());
	memcpy(&(m_ExpectedValue[Samples1->GetNumSamples()]),Samples2->m_ExpectedValue,sizeof(double)*Samples2->GetNumSamples());

	// Copy the images
	memcpy(m_SamplesSet,Samples1->m_SamplesSet,Samples1->GetNumSamples()*m_DataLen);
	memcpy(&(m_SamplesSet[Samples1->GetNumSamples()*m_DataLen]),Samples2->m_SamplesSet,Samples2->GetNumSamples()*m_DataLen);
}

void Evolutive::CSamplesSet::UpdateSamples(CClassifier *Classifier)
{
	register int i;
	CData *pData=NULL;
	int Count=0;

	// Check if the detector is assigned
	if(!m_Detector)
		throw CEvolutiveLibException("NULL pointer to the detector object",__FILE__,__LINE__,"UpdateSamples");

	// Store the classifier in the detector object
	m_Detector->SetClassifier(Classifier);

	// Change the negative samples that are well classified
	cout << "Updating the samples set with new BG regions...." << endl;
	for(i=0;i<m_NumImages;i++)
	{
		if(m_ExpectedValue[i]<0)
		{
			// Point the data
			pData=GetData(i);

			if(Classifier->Apply(pData)<0)
			{
				// Change this sample by using one of the background samples
				ChangeSample(i);
				Count++;
			}

			// Release the data
			delete pData;
		}
	}
	cout << "   " << Count << " samples replaced." << endl;
}

void Evolutive::CSamplesSet::ResetBGFiles(void)
{
	m_BGFiles.clear();
}

void Evolutive::CSamplesSet::LoadBGFiles(string FileName)
{
	fstream fin;
	string TextLine;

	// Open the file
	fin.open(FileName.data(),ios_base::in);
	if(fin.fail())
		throw CEvolutiveLibException("Cannot open input file.",__FILE__,__LINE__,"LoadBGFiles");

	// Process all the file
	while(!fin.eof())
	{
		// Read a line from the file
		getline(fin,TextLine);

		// Discard empty lines and comments lines
		if(TextLine.size()>0 && *(TextLine.data())!='#')
			m_BGFiles.push_back(TextLine);
	}

	// Close the file
	fin.close();
}

void Evolutive::CSamplesSet::SetDetector(CDetector *Detector)
{
	m_Detector=Detector;
}

void Evolutive::CSamplesSet::FindNewBGRegions(int NumRegions)
{
	CBGRect NewReg;
	IplImage *BGImage=NULL;
	CRectVector Regions;
	register int i;

	// Check if there are Background images
	if(!m_BGFiles.size())
		throw CEvolutiveLibException("No background file is defined.",__FILE__,__LINE__,"FindNewBGRegions");		
	
	// Remove the regions
	m_BGRegions.clear();

	cout << "Looking for new BG regions ..." << endl;
	while(m_BGRegions.size()<NumRegions && m_IdxBGFile<static_cast<int>(m_BGFiles.size()))
	{
		// Increment the background indexes
		m_IdxBGFile++;

		// If no other files are available show an error message
		if(m_IdxBGFile>=m_BGFiles.size())
			throw CEvolutiveLibException("Not enough background files.",__FILE__,__LINE__,"FindNewBGRegions");		

		// Load the next BG image
		BGImage=cvLoadImage(m_BGFiles[m_IdxBGFile].data(),m_UseColor);

		// Verify the BG image
		if(!BGImage)
			throw CEvolutiveLibException("Cannot open Background image.",__FILE__,__LINE__,"FindNewBGRegions");
		
		// Apply the detector
		m_Detector->FindRegions(&Regions,BGImage);

		// Add the regions to the regions vector
		for(i=0;i<Regions.size();i++)
		{
			NewReg.Region=Regions[i];
			NewReg.FileIdx=m_IdxBGFile;
			NewReg.Used=false;
			m_BGRegions.push_back(NewReg);
		}
	}
	cout << "   " << m_BGRegions.size() << " new BG regions have been added" << endl;

	// Add all the new regions as available regions
	m_AvailableBGRegions=m_BGRegions.size();

	// If there are no available region stop
	if(!m_AvailableBGRegions)
		throw CEvolutiveLibException("Not enough Background images.",__FILE__,__LINE__,"FindNewBGRegions");
}

void Evolutive::CSamplesSet::ChangeSample(int Idx)
{
	int NumRegions;
	int RndIdx;
	bool SampleOK;
	IplImage *Image=NULL;
	int LastBGLoaded=-1;
	IplImage *RegImg=NULL;
	double ClassVal;

	// Set the number of regions to look for as the half of the samples set
	NumRegions=cvRound(m_NumImages/2.0);

	// Find a regions that fails
	SampleOK=false;
	while(!SampleOK)
	{
		// If no regions are available find new regions
		if(m_AvailableBGRegions<=0)
		{
			FindNewBGRegions(NumRegions);
		}

		// Select a random region
		RndIdx=(m_BGRegions.size()-1)*RAND_VALUE();
		RndIdx=cvRound(RndIdx);

		// Find the nearest available region
		while(m_BGRegions[RndIdx].Used)
		{
			RndIdx++;
			if(RndIdx>=m_BGRegions.size())
				RndIdx=0;
		}

		// Mark this region as used
		m_BGRegions[RndIdx].Used=true;
		m_AvailableBGRegions--;

		// Load the image
		if(LastBGLoaded!=m_BGRegions[RndIdx].FileIdx)
		{
			LastBGLoaded=m_BGRegions[RndIdx].FileIdx;
			Image=cvLoadImage(m_BGFiles[LastBGLoaded].data(),m_UseColor);
		}

		// Copy this image to the destination position
		RegImg=GetImage(Idx);
		cvSetImageROI(Image,m_BGRegions[RndIdx].Region);
		cvResize(Image,RegImg,CV_INTER_CUBIC);
		cvResetImageROI(Image);

		// Verify if this image is miss classified
		if(m_Detector->GetClass(RegImg)>0)
			SampleOK=true;
	}

	// Release temporal images
	cvReleaseImage(&Image);
}
#endif//USE_OPENCV
